{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# Jinja Template Language 金贾Jinja模板语言基础\r\n",
    "\r\n",
    "Jinja 模板语言是一个小的构造集合，它可以帮助我们自动创建模板。\r\n",
    "\r\n",
    "双花括号{{}}允许我们计算一个表达式、变量或函数调用并将结果输出到模板中:"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "source": [
    "from IPython.core.interactiveshell import InteractiveShell\r\n",
    "InteractiveShell.ast_node_interactivity = \"all\"  # 设置后 notebook 会显示每个变量的输出, 不然默认只显示最后一个变量的输出"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 计算表达式"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "source": [
    "from jinja2 import Template\r\n",
    "Template(\"{{ 10 + 3 }}\").render()\r\n",
    "Template(\"{{ 10 - 3 }}\").render()\r\n",
    "Template(\"{{ 10 // 3 }}\").render()\r\n",
    "Template(\"{{ 10 / 3 }}\").render()\r\n",
    "Template(\"{{ 10 % 3 }}\").render()\r\n",
    "Template(\"{{ 10 ** 3 }}\").render()"
   ],
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "'13'"
      ]
     },
     "metadata": {},
     "execution_count": 1
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "'7'"
      ]
     },
     "metadata": {},
     "execution_count": 1
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "'3'"
      ]
     },
     "metadata": {},
     "execution_count": 1
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "'3.3333333333333335'"
      ]
     },
     "metadata": {},
     "execution_count": 1
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "'1'"
      ]
     },
     "metadata": {},
     "execution_count": 1
    },
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "'1000'"
      ]
     },
     "metadata": {},
     "execution_count": 1
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "其他 Python 操作符，如 Comparision、 Logical 和 Membership 操作符也可以在表达式中使用。\r\n",
    "\r\n",
    "## 评估变量"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# Evaluating Variables\r\n",
    "Template(\"{{ var }}\").render(var=12)\r\n",
    "Template(\"{{ var }}\").render(var=\"hello\")"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "我们不仅仅局限于数字和字符串，金贾模板还可以处理复杂的数据结构，如列表、字典、元组，甚至定制类。"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "Template(\"{{ var[1] }}\").render(var=[1,2,3])\r\n",
    "Template(\"{{ var['profession'] }}\").render(var={'name':'tom', 'age': 25, 'profession': 'Manager' })\r\n",
    "Template(\"{{ var[2] }}\").render(var=(\"c\", \"c++\", \"python\"))\r\n",
    "\r\n",
    "class Foo:\r\n",
    "    def  __str__(self):\r\n",
    "        return \"This is an instance of Foo class\"\r\n",
    "Template(\"{{ var }}\").render(var=Foo())\r\n",
    "\r\n",
    "# 如果索引无效，金贾将默认输出一个空字符串。\r\n",
    "Template(\"{{ var[100] }}\").render(var=(\"c\", \"c++\", \"python\"))\r\n"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 函数调用"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "\r\n",
    "# 函数调用也一样\r\n",
    "def foo():\r\n",
    "    return \"foo() called\"\r\n",
    "\r\n",
    "Template(\"{{ foo() }}\").render(foo=foo)    "
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 属性和方法\r\n",
    "\r\n",
    "若要访问对象的属性和方法，请使用点(.)运算符。"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "class Foo:\r\n",
    "    def __init__(self, i):\r\n",
    "        self.i = i\r\n",
    "    def do_something(self):\r\n",
    "        return \"do_something() called\"\r\n",
    "\r\n",
    "Template(\"{{ obj.i }}\").render(obj=Foo(5))        \r\n",
    "\r\n",
    "Template(\"{{ obj.do_something() }}\").render(obj=Foo(5))"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 注释的语法\r\n",
    "\r\n",
    "```\r\n",
    "{# comment in a line #}\r\n",
    "\r\n",
    "{#\r\n",
    "    comment expanded \r\n",
    "    to multiple\r\n",
    "    lines\r\n",
    "#}\r\n",
    "```"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 设置变量\r\n",
    "\r\n",
    "在模板中，我们可以使用 set 语句定义一个变量。\r\n",
    "\r\n",
    "```\r\n",
    "{% set fruit = 'apple' %}\r\n",
    "{% set name, age = 'Tom', 20 %} {# tuple unpacking works inside templates too #}\r\n",
    "```\r\n",
    "\r\n",
    "我们定义变量来存储某些复杂操作的结果，以便可以跨模板 重用 `reused`  它。控制结构之外定义的变量(在下面讨论)充当全局变量，可以在任何控制结构中访问。但是，在控制结构中创建的变量作为局部变量，并且只在定义它的控制结构中可见，这个规则的唯一例外是 if 语句。\r\n",
    "\r\n",
    "## 控制结构\r\n",
    "\r\n",
    "控制结构允许我们在模板中添加控制流和循环。默认情况下，控件结构使用`{% ...% }`分隔符，而不是双花括号 `{{ ... }}` 。\r\n",
    "\r\n",
    "### If 语句\r\n",
    "\r\n",
    "```\r\n",
    "{% if bookmarks %}\r\n",
    "    <p>User has some bookmarks</p>\r\n",
    "{% endif %}\r\n",
    "```\r\n",
    "在 Jinja  ，如果没有定义一个变量，那么它的结果就是 false。\r\n",
    "\r\n",
    "#### elif 和 else 子句:\r\n",
    "```\r\n",
    "{% if user.newbie %}\r\n",
    "    <p>Display newbie stages</p>\r\n",
    "{% elif user.pro %}\r\n",
    "    <p>Display pro stages</p>\r\n",
    "{% elif user.ninja %}\r\n",
    "    <p>Display ninja stages</p>\r\n",
    "{% else %}\r\n",
    "    <p>You have completed all stages</p>    \r\n",
    "{% endif %}\r\n",
    "```\r\n",
    "控制语句也可以嵌套。例如:\r\n",
    "```\r\n",
    "{% if user %}\r\n",
    "    {% if user.newbie %}\r\n",
    "        <p>Display newbie stages</p>\r\n",
    "    {% elif user.pro %}\r\n",
    "        <p>Display pro stages</p>\r\n",
    "    {% elif user.ninja %}\r\n",
    "        <p>Display ninja stages</p>\r\n",
    "    {% else %}\r\n",
    "        <p>You have completed all states</p>\r\n",
    "    {% endif %}\r\n",
    "{% else %}\r\n",
    "    <p>User is not defined</p>\r\n",
    "{% endif %}\r\n",
    "```\r\n",
    "在某些情况下，在一行中添加 if 语句非常有用。金贾支持 inline if 语句，但是如果表达式使用双花括号`{{ ... }}`而不是`{% ...% }`创建，则调用 inline if 语句。例如:\r\n",
    "```{{ \"User is logged in\" if loggedin else \"User is not logged in\"  }}```\r\n",
    "Else 子句是可选的，如果没有提供，则 else 块将被计算为未定义的对象。\r\n",
    "` {{ \"User is logged in\" if loggedin  }}`\r\n",
    "\r\n",
    "### 逻辑符号\r\n",
    "\r\n",
    "就像 Python 一样，我们可以在控制结构中使用 `Comparision` 、 `Logical` 和 `Membership` 操作符来创建更复杂的条件。以下是一些例子:\r\n",
    "\r\n",
    "```html\r\n",
    "{# if user.count is equal to 1000, '<p>User count is 1000</p>' will be printed #} # 这行是注释\r\n",
    "{% if users.count == 1000 %}\r\n",
    "    <p>User count is 1000</p>\r\n",
    "{%  endif %}\r\n",
    "\r\n",
    "\r\n",
    "{# expression 10 >= 2 is true so '<p>10 >= 2</p>' will be printed #}\r\n",
    "{% if 10 >= 2 %}\r\n",
    "    <p>10 >= 2</p>\r\n",
    "{%  endif %}\r\n",
    "\r\n",
    "\r\n",
    "{# expression \"car\" <= \"train\" is true so '<p>car <= train</p>' will be printed #}\r\n",
    "{% if \"car\" <= \"train\" %}\r\n",
    "    <p>car <= train</p>\r\n",
    "{%  endif %}\r\n",
    "\r\n",
    "\r\n",
    "{# \r\n",
    "    if user is logged in and is a superuser, \r\n",
    "    '<p>User is logged in and is a superuser</p>' will be printed \r\n",
    "#}\r\n",
    "{% if user.loggedin and user.is_superuser %}\r\n",
    "    <p>User is logged in and is a superuser</p>\r\n",
    "{% endif %}\r\n",
    "\r\n",
    "\r\n",
    "{# \r\n",
    "    if user is superuser or moderator or author \r\n",
    "    '<a href=\"#\">Edit</a>' will be printed \r\n",
    "#}\r\n",
    "{% if user.is_superuser or user.is_moderator or user.is_author %}\r\n",
    "    <a href=\"#\">Edit</a>\r\n",
    "{%  endif %}\r\n",
    "\r\n",
    "\r\n",
    "{# \r\n",
    "    if user and current_user points to the same object\r\n",
    "    <p>user and current_user are same</p> will be printed \r\n",
    "#}\r\n",
    "{% if user is current_user  %}\r\n",
    "    <p>user and current_user are same</p>\r\n",
    "{%  endif %}\r\n",
    "\r\n",
    "\r\n",
    "{# \r\n",
    "    As \"Flask\" is one of element in dictionary\r\n",
    "    '<p>Flask is in the dictionary</p>' will be printed \r\n",
    "#}\r\n",
    "{% if [\"Flask\"] in [\"Django\", \"web2py\", \"Flask\"] %}\r\n",
    "    <p>Flask is in the dictionary</p>\r\n",
    "{%  endif %}\r\n",
    "```\r\n",
    "\r\n",
    "如果你的条件变得太复杂，或者你只是想改变运算符的优先级，你可以把你的表达式包装在括号内如下:\r\n",
    "\r\n",
    "```\r\n",
    "{% if (user.marks > 80) and (user.marks < 90)  %}\r\n",
    "    <p>You grade is B</p>\r\n",
    "{%  endif %}\r\n",
    "```"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## For loop 循环\r\n",
    "\r\n",
    "循环允许我们迭代一个序列。例如:\r\n",
    "\r\n",
    "### 在list上迭代\r\n",
    "```\r\n",
    "{% set user_list = ['tom','jerry', 'spike'] %}\r\n",
    "\r\n",
    "<ul>\r\n",
    "{% for user in user_list %}\r\n",
    "    <li>{{ user }}</li>\r\n",
    "{% endfor %}\r\n",
    "</ul>\r\n",
    "```\r\n",
    "\r\n",
    "### 在字典上迭代\r\n",
    "```\r\n",
    "{% set employee = { 'name': 'tom', 'age': 25, 'designation': 'Manager' } %}\r\n",
    "\r\n",
    "<ul>\r\n",
    "{% for key in employee.items() %}   \r\n",
    "    这里可能为keys()\r\n",
    "    <li>{{ key }} : {{ employee[key] }}</li>\r\n",
    "{% endfor %}\r\n",
    "</ul>\r\n",
    "```\r\n",
    "\r\n",
    "```\r\n",
    "{% set employee = { 'name': 'tom', 'age': 25, 'designation': 'Manager' } %}\r\n",
    "\r\n",
    "<ul>\r\n",
    "{% for key, value in employee.items() %}\r\n",
    "    <li>{{ key }} : {{ value }}</li>\r\n",
    "{% endfor %}\r\n",
    "</ul>\r\n",
    "```\r\n",
    "### For ... else 子句\r\n",
    "\r\n",
    "For 循环也可以像 Python 一样接受一个可选的 else 子句，但是它的用法略有不同。\r\n",
    "\r\n",
    "回想一下，在 `Python` 中，当 for 循环后面跟着 else 子句时，只有当 for 循环在序列上循环后终止或序列为空时，才会执行 else 子句。当 for 循环被 break 语句终止时，不会执行它。\r\n",
    "在 `jinja` 中, 当 else 子句与 for 循环一起使用时，它只在序列为空或未定义时执行。例如:\r\n",
    "\r\n",
    "```\r\n",
    "{% set user_list = [] %}\r\n",
    "\r\n",
    "<ul>\r\n",
    "{% for user in user_list  %}\r\n",
    "    <li>{{ user }}</li>\r\n",
    "{% else %}\r\n",
    "    <li>user_list is empty</li>\r\n",
    "{% endfor %}\r\n",
    "</ul>\r\n",
    "```\r\n",
    "\r\n",
    "### loop 的特殊变量\r\n",
    "\r\n",
    "For 循环使您可以访问一个称为 loop 的特殊变量，以跟踪循环的进度。例如:\r\n",
    "\r\n",
    "```\r\n",
    "<ul>\r\n",
    "{% for user in user_list  %}\r\n",
    "    <li>{{ loop.index }} - {{ user }}</li>\r\n",
    "{% endfor %}\r\n",
    "</ul>\r\n",
    "```\r\n",
    "\r\n",
    "For 循环中的 loop.index 返回从1开始的循环的当前迭代。下表列出了循环变量的其他常用属性。\r\n",
    "\r\n",
    "| Variable         | Description                                                  |\r\n",
    "| :--------------- | :----------------------------------------------------------- |\r\n",
    "| `loop.index0`    | same as `loop.index` but 0 indexed i.e it starts counting iteration from 0 instead of 1. |\r\n",
    "| `loop.revindex`  | returns the iteration from the end of the loop (1 indexed).  |\r\n",
    "| `loop.revindex0` | same as `loop.revindex` but 0 indexed.                       |\r\n",
    "| `loop.first`     | returns `True` if the current iteration is the first iteration. Otherwise `False`. |\r\n",
    "| `loop.last`      | returns `True` if the current iteration is the last iteration. Otherwise `False`. |\r\n",
    "| `loop.length`    | returns the length of the sequence the for loop is iterating over. |\r\n",
    "| `loop.index0`    | 和 `loop.index` 一样 但是0索引, 即开始计算从0开始的迭代而不是1 |\r\n",
    "| `loop.revindex`  | 返回从循环末尾开始的迭代(1个索引)                  |\r\n",
    "| `loop.revindex0` | 和 `loop.revindex` 一样 但是0索引                               |\r\n",
    "| `loop.first`     | 返回 `True` 如果当前迭代是第一次迭代，否则`False`.          |\r\n",
    "| `loop.last`      | 返回 `True` 如果当前迭代是最后一次迭代，否则`False`.        |\r\n",
    "| `loop.length`    | 返回 for 循环遍历的序列的长度                      |\r\n",
    "\r\n"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Filters 过滤器\r\n",
    "\r\n",
    "过滤器在变量呈现之前修改它们。使用过滤器的语法如下:\r\n",
    "\r\n",
    "`variable_or_value|filter_name`\r\n",
    "\r\n",
    "左边为变量, 右边为一个filter的名字\r\n",
    "\r\n",
    "下面是一个例子:\r\n",
    "\r\n",
    "`{{ comment|title }}`  # `title` 过滤器 将每个单词的第一个字符大写，因此如果变量 `comment` 设置为 “dust in the wind”，那么输出将为“ Dust In The Wind”。\r\n",
    "\r\n",
    "我们还可以 链接调用(chain) 多个过滤器 来修改输出。例如:\r\n",
    "\r\n",
    "`{{ full_name|striptags|title }}`  # `Striptags` 过滤器从变量中删除所有 HTML 标记。在上面的代码中，striptags 过滤器将首先应用，然后是 `title` 过滤器。\r\n",
    "\r\n",
    "有些过滤器也可以接受参数。为了将参数传递给过滤器，可以像调用函数一样调用它。例如:\r\n",
    "\r\n",
    "`{{ number|round(2) }}`  # 舍入过滤器将数字舍入到指定的位数(这里是2位小数)。\r\n",
    "\r\n",
    "下表列出了一些常用的过滤器。\r\n",
    "\r\n",
    "| Name 姓名    | Description 描述                                             |\r\n",
    "| :----------- | :----------------------------------------------------------- |\r\n",
    "| `upper`      | converts all the characters to uppercase. 将所有字符转换为大写 |\r\n",
    "| `lower`      | converts all the characters to lowercase. 将所有字符转换成小写 |\r\n",
    "| `capitalize` | capitalizes the first character and converts rest of the characters to lowercase. 将第一个字符大写，并将其余字符转换为小写 |\r\n",
    "| `escape`     | escapes the value 逃避价值                                   |\r\n",
    "| `safe`       | prevents escaping 防止逃跑                                   |\r\n",
    "| `length`     | returns the number of elements in the sequence 返回序列中元素的数量 |\r\n",
    "| `trim`       | removes leading and trailing whitespace characters 删除前导和尾随空格字符 |\r\n",
    "| `random`     | returns a random item from the sequence 从序列中返回一个随机项 |\r\n",
    "\r\n",
    "注意: 内置过滤器的完整列表可以在这里找到。\r\n",
    "\r\n",
    "https://jinja.palletsprojects.com/en/3.0.x/templates/#list-of-builtin-filters\r\n",
    "\r\n",
    "\r\n",
    "| [`abs()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.abs) | [`float()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.float) | [`lower()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.lower) | [`round()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.round) | [`tojson()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.tojson) |\r\n",
    "| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |\r\n",
    "| [`attr()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.attr) | [`forceescape()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.forceescape) | [`map()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.map) | [`safe()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.safe) | [`trim()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.trim) |\r\n",
    "| [`batch()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.batch) | [`format()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.format) | [`max()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.max) | [`select()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.select) | [`truncate()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.truncate) |\r\n",
    "| [`capitalize()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.capitalize) | [`groupby()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.groupby) | [`min()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.min) | [`selectattr()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.selectattr) | [`unique()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.unique) |\r\n",
    "| [`center()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.center) | [`indent()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.indent) | [`pprint()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.pprint) | [`slice()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.slice) | [`upper()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.upper) |\r\n",
    "| [`default()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.default) | [`int()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.int) | [`random()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.random) | [`sort()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.sort) | [`urlencode()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.urlencode) |\r\n",
    "| [`dictsort()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.dictsort) | [`join()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.join) | [`reject()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.reject) | [`string()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.string) | [`urlize()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.urlize) |\r\n",
    "| [`escape()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.escape) | [`last()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.last) | [`rejectattr()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.rejectattr) | [`striptags()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.striptags) | [`wordcount()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.wordcount) |\r\n",
    "| [`filesizeformat()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.filesizeformat) | [`length()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.length) | [`replace()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.replace) | [`sum()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.sum) | [`wordwrap()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.wordwrap) |\r\n",
    "| [`first()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.first) | [`list()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.list) | [`reverse()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.reverse) | [`title()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.title) | [`xmlattr()`](https://jinja.palletsprojects.com/en/3.0.x/templates/#jinja-filters.xmlattr) |\r\n",
    "\r\n"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Macros 宏\r\n",
    "\r\n",
    "Jinjia的宏类似于 Python 中的函数。这个想法是通过给代码命名来创建可重用(reusable)的代码。例如:\r\n",
    "\r\n",
    "```jinja2\r\n",
    "{% macro render_posts(field, sep=False) %}  {# 注意为 单大括号}\r\n",
    "    <div>\r\n",
    "        {% for post in post_list %} \r\n",
    "            <h2>{{ post.title }}</h2> \r\n",
    "            <article>\r\n",
    "                {{ post.html|safe }} {# 注意为 双大括号}\r\n",
    "            </article>\r\n",
    "        {% endfor %}\r\n",
    "        {% if sep %}<hr>{% endif %} {# 注意为 单大括号}\r\n",
    "    </div>\r\n",
    "{% endmacro %} # 注意为 单大括号\r\n",
    "```\r\n",
    "\r\n",
    "这里我们创建了一个名为 render _ posts 的宏，它接受一个名为 post _ list 的必需参数和一个名为 sep. 的可选参数。要使用宏调用它，请如下:\r\n",
    "\r\n",
    "`{{ render_posts(posts) }}`\r\n",
    "\r\n",
    "宏定义必须在使用之前出现，否则会出现错误。\r\n",
    "\r\n",
    "与其在模板中添加宏，不如将它们存储在单独的文件中，然后根据需要导入文件。\r\n",
    "\r\n",
    "假设我们已经将所有宏存储在模板目录中名为宏的文件中。现在要从宏导入宏，我们使用 import 语句如下:\r\n",
    "\r\n",
    "`{% import \"macros.html\" as macros %}`  # 注意为 双大括号\r\n",
    "\r\n",
    "我们现在可以使用宏变量引用在宏中定义的宏。例如:\r\n",
    "\r\n",
    "`{{ macros.render_posts(posts) }}`      # 注意为 双大括号\r\n",
    "\r\n",
    "Import 语句 `{% import \"macros.html\" as macros %}` 将 `macros.html` (在顶级定义)中的所有宏和变量导入到模板中。\r\n",
    "\r\n",
    "或者，我们也可以使用 Import 语句在模板中 `只导入部分` 选定的名称，如下所示:\r\n",
    "\r\n",
    "{% from \"macros.html\" import render_posts  %}\r\n",
    "\r\n",
    "在使用宏时，您会遇到需要向宏传递任意数量的参数的情况。\r\n",
    "\r\n",
    "\r\n",
    "类似于 Python 中的 *args 和 **kwargs，在宏中您可以访问 varargs 和 kwargs。\r\n",
    "\r\n",
    "Varargs: 它捕获作为元组(tuple)传递给宏的附加位置参数。 如: (200,400)\r\n",
    "\r\n",
    "Kwargs: 它捕获作为字典(dictionary)传递给宏的附加关键字参数。如: {'width':200,'heigh':400}\r\n",
    "\r\n",
    "虽然您可以访问宏中的这两个变量，但是不需要在宏标头中显式声明它们。下面是一个例子:\r\n",
    "\r\n",
    "```jinja2\r\n",
    "{% macro custom_renderer(para) %}  {# 单大括号 #}\r\n",
    "    <p>{{ para }}</p>   {# 双大括号 #}\r\n",
    "    <p>varargs: {{ varargs }}</p>  \r\n",
    "    <p>kwargs: {{ kwargs }}</p>\r\n",
    "{% endmacro %}\r\n",
    "\r\n",
    "{{ custom_renderer(10, \"apple\", name='spike', age=15) }}   # 注意为 双大括号\r\n",
    "```\r\n",
    "\r\n",
    "在这种情况下，将额外的位置参数(如 \"apple\" )分配给 varargs，并将额外的关键字参数(name='spike', age=15)分配给 kwargs。\r\n",
    "注: 这里的 10 直接分配给了 para (做为固定参数)\r\n",
    "\r\n"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Escaping 转义\r\n",
    "\r\n",
    "默认情况下，出于安全考虑，金贾会自动转义变量并输出。因此，如果一个变量有一个包含 HTML 的字符串，比如 `\"<p>Escaping in Jinja</p>\"` ，那么它将被显示为 `\"<p>&lt;p&gt;Escaping in Jinja&lt;/p&gt;</p>\"` 。这将导致 HTML 标记在浏览器中显示，而不是解释。如果您知道数据是安全的，并且希望按原样呈现而不想转义，那么可以使用 `safe` (安全) `filter` 过滤器。例如:\r\n",
    "\r\n",
    "```jinja2\r\n",
    "{% set html = <p>Escaping in Jinja</p> %}\r\n",
    "{{ html|safe }}  # safe 为一个过滤器,参考前2节的filter\r\n",
    "```\r\n",
    "\r\n",
    "然而, 重复地对大块内容应用 `safe` 过滤器可能既笨拙又不方便，这就是为什么`金贾`提供了 `autoescape` 语句来 转义 或 不转义 (escape or unescape ) 大块内容。它接受 `true` 或 `false` 作为参数，分别打开和关闭自动转义。例如:\r\n",
    "\r\n",
    "```jinja2\r\n",
    "{% autoescape true %}\r\n",
    "    Escaping enabled\r\n",
    "{% endautoescape %}\r\n",
    "\r\n",
    "{% autoescape false %}\r\n",
    "    Escaping disabled\r\n",
    "{% endautoescape %}\r\n",
    "```\r\n",
    "\r\n",
    "在 `{%autoescape false% } 和 {%endautoescape%}` 之间的所有内容将按原样呈现而不进行转义。如果您想在关闭自动转义时转义某些元素，请使用 `escape`  过滤器。例如:\r\n",
    "\r\n",
    "```jinja2\r\n",
    "{% autoescape false %}\r\n",
    "    <div class=\"post\">\r\n",
    "        {% for post in post_list %}\r\n",
    "            <h2>{{ post.title }}</h2>\r\n",
    "            <article>\r\n",
    "                {{ post.html }}\r\n",
    "            </article>\r\n",
    "        {% endfor %}        \r\n",
    "    </div>\r\n",
    "    <div>\r\n",
    "        {% for comment in comment_list %}\r\n",
    "            <p>{{ comment|escape }}</p>   # 这里`escape`转义过滤器已打开 \r\n",
    "        {% endfor %}\r\n",
    "    </div>\r\n",
    "{% endautoescape %}\r\n",
    "\r\n"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Including templates 包括模板\r\n",
    "\r\n",
    "`Include` 语句在另一个模板中呈现模板。 `Include` 语句通常用于渲染整个站点重复的静态部分。下面是 `include` 语句的语法:\r\n",
    "\r\n",
    "`{% include 'path/to/template' %}`\r\n",
    "\r\n",
    "假设我们有一个简单的导航栏存储在模板 `templates`  目录的 `nav.html` 文件中，如下所示:\r\n",
    "\r\n",
    "```html\r\n",
    "<nav>\r\n",
    "    <a href=\"/home\">Home</a>\r\n",
    "    <a href=\"/blog\">Blog</a>\r\n",
    "    <a href=\"/contact\">Contact</a>  \r\n",
    "</nav>\r\n",
    "```\r\n",
    "\r\n",
    "为了将导航条包含在 home. html 中，我们使用以下代码:\r\n",
    "```html\r\n",
    "<!DOCTYPE html>\r\n",
    "<html lang=\"en\">\r\n",
    "<head>\r\n",
    "    <meta charset=\"UTF-8\">\r\n",
    "    <title>Title</title>\r\n",
    "</head>\r\n",
    "<body>\r\n",
    "\r\n",
    "    {# including the navigation bar from nav.html #}\r\n",
    "    {% include 'nav.html' %}    {# 单大括号 #}\r\n",
    "\r\n",
    "</body>\r\n",
    "</html>\r\n",
    "```\r\n"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Template Inheritance 模板继承\r\n",
    "\r\n",
    "模板继承是 Jinja Templating 最强大的一个方面。模板继承背后的思想有点类似于面向对象编程。\r\n",
    "我们首先创建一个基本模板，其中包含HTML骨架(skeleton)和子模板可以覆盖的标记(markers)。标记是使用 `block` 语句创建的。子模板使用 `extends` 语句来继承或扩展基模板。让我们举个例子:\r\n",
    "\r\n",
    "```html\r\n",
    "{# This is templates/base.html template #}\r\n",
    "<!DOCTYPE html>\r\n",
    "<html lang=\"en\">\r\n",
    "<head>\r\n",
    "    <meta charset=\"UTF-8\">\r\n",
    "    <title>{% block title %}Default Title{% endblock %}</title>\r\n",
    "</head>\r\n",
    "<body>\r\n",
    "\r\n",
    "    {% block nav %}\r\n",
    "        <ul>\r\n",
    "            <li><a href=\"/home\">Home</a></li>\r\n",
    "            <li><a href=\"/api\">API</a></li>\r\n",
    "        </ul>\r\n",
    "    {% endblock %}\r\n",
    "\r\n",
    "    {% block content %}\r\n",
    "\r\n",
    "    {% endblock %}\r\n",
    "\r\n",
    "</body>\r\n",
    "</html>\r\n",
    "```\r\n",
    "\r\n",
    "这是我们的基本模板页 `base.html` 。它使用子模板可以填充的 `block` 语句定义了三个块。 `block` 语句带了一个参数，这个参数是块的名称。在模板内部，块名称必须是唯一的，否则会出现错误。\r\n",
    "\r\n",
    "子模板是扩展基本模板的模板。子模板可以添加、重写或保留父块的内容。下面是我们如何创建一个子模板。\r\n",
    "\r\n",
    "```html\r\n",
    "{# this is templates/child.html template #}\r\n",
    "{% extends 'base.html' %}  # 扩展自'base.html' \r\n",
    "\r\n",
    "{% block content %}\r\n",
    "    {% for bookmark in bookmarks %}\r\n",
    "        <p>{{ bookmark.title }}</p>\r\n",
    "    {% endfor %}\r\n",
    "{% endblock %}\r\n",
    "```\r\n",
    "\r\n",
    "`Extends` 语句告诉Jinja `child.html` 是一个子模板并继承自 `base.html` 。当金贾遇到 extends 语句时，它加载基模板, 这里是`base.html`，然后用子模板中相同名称的内容块替换父模板中的内容块。如果在子模板中没有找到匹配的名称块，那么将使用来自父模板的块。\r\n",
    "\r\n",
    "请注意，在子模板中，我们只覆盖 `content` 块，因此在呈现子模板时将使用父模板的 `title` 和 `nav` 的默认内容。输出应该是这样的:\r\n",
    "\r\n",
    "```html\r\n",
    "<head>\r\n",
    "    <meta charset=\"UTF-8\">\r\n",
    "    <title>Default Title</title>\r\n",
    "</head>\r\n",
    "<body>\r\n",
    "    \r\n",
    "    <ul>\r\n",
    "        <li><a href=\"/home\">Home</a></li>\r\n",
    "        <li><a href=\"/api\">API</a></li>\r\n",
    "    </ul>\r\n",
    "\r\n",
    "    <p>Bookmark title 1</p>\r\n",
    "    <p>Bookmark title 2</p>\r\n",
    "    <p>Bookmark title 3</p>\r\n",
    "    <p>Bookmark title 4</p>\r\n",
    "\r\n",
    "\r\n",
    "</body>\r\n",
    "</html>\r\n",
    "```\r\n",
    "\r\n",
    "如果我们愿意，我们可以通过覆盖 child. html 中的 title 块来更改默认的 title，如下所示:\r\n",
    "\r\n",
    "```\r\n",
    "{# this is templates/child.html template #}\r\n",
    "{% extends 'base.html' %}\r\n",
    "\r\n",
    "{% block title %}  {# 关注这段 #}\r\n",
    "    Child Title \r\n",
    "{% endblock %}\r\n",
    "\r\n",
    "{% block content %}\r\n",
    "    {% for bookmark in bookmarks %}\r\n",
    "        <p>{{ bookmark.title }}</p>\r\n",
    "    {% endfor %}\r\n",
    "{% endblock %}\r\n",
    "```\r\n",
    "\r\n",
    "在重写一个块之后，您仍然可以通过调用 `super()` 函数来引用父模板中的内容。 `Super()` 调用通常用于子模板需要在来自父模板的内容之外再增加自己的内容时。\r\n",
    "\r\n",
    "例如:\r\n",
    "\r\n",
    "```jinja2\r\n",
    "{# this is templates/child.html template #}\r\n",
    "{% extends 'base.html' %}\r\n",
    "\r\n",
    "{% block title %}\r\n",
    "    Child Title \r\n",
    "{% endblock %}\r\n",
    "\r\n",
    "{% block nav %} \r\n",
    "    {{ super() }} {# referring to the content in the parent templates 引用父模板中的内容 #}\r\n",
    "    <li><a href=\"/contact\">Contact</a></li>\r\n",
    "    <li><a href=\"/career\">Career</a></li>\r\n",
    "{% endblock %}\r\n",
    "\r\n",
    "{% block content %}\r\n",
    "    {% for bookmark in bookmarks %}\r\n",
    "        <p>{{ bookmark.title }}</p>\r\n",
    "    {% endfor %}\r\n",
    "{% endblock %}\r\n",
    "```\r\n",
    "\r\n",
    "输出:\r\n",
    "```html\r\n",
    "<head>\r\n",
    "    <meta charset=\"UTF-8\">\r\n",
    "    <title>Child Title</title>\r\n",
    "</head>\r\n",
    "<body>\r\n",
    "    \r\n",
    "    <ul>\r\n",
    "        <li><a href=\"/home\">Home</a></li>\r\n",
    "        <li><a href=\"/api\">API</a></li>\r\n",
    "        <li><a href=\"/contact\">Contact</a></li>\r\n",
    "        <li><a href=\"/career\">Career</a></li>\r\n",
    "    </ul>\r\n",
    "\r\n",
    "    <p>Bookmark title 1</p>\r\n",
    "    <p>Bookmark title 2</p>\r\n",
    "    <p>Bookmark title 3</p>\r\n",
    "    <p>Bookmark title 4</p>\r\n",
    "\r\n",
    "\r\n",
    "</body>\r\n",
    "</html>\r\n",
    "```\r\n"
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "这就是你需要知道的关于jinja模板的一切。在接下来的课程中，我们将使用这些知识来创建一些很棒的模板。"
   ],
   "metadata": {}
  }
 ],
 "metadata": {
  "orig_nbformat": 4,
  "language_info": {
   "name": "python",
   "version": "3.9.6",
   "mimetype": "text/x-python",
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "pygments_lexer": "ipython3",
   "nbconvert_exporter": "python",
   "file_extension": ".py"
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3.9.6 64-bit"
  },
  "interpreter": {
   "hash": "c644d696b95f5e0f4df3c6556741cf30bcf9ea6ca93c3e1f29fcf31d885534fc"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}